home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Carousel
/
CAROUSEL.cdr
/
mactosh
/
util
/
hdrunner.sit
/
HD Runner.c
< prev
next >
Wrap
C/C++ Source or Header
|
1987-04-13
|
23KB
|
925 lines
/*
* HD Runner: An application that scans the desktop to retrieve the names and locations of
* all applications on HFS volumes, and offers a scrolling list for launching.
* Author: Dewi Williams Delphi: dewi, CompuServe 73210,132.
* Status: Explicitly in public domain. Please keep the application, source and
* documentation together when distributing.
* Limitations: ignores MFS volumes.
* Build: Lightspeed C, version 2.01. Needs Lightspeed's qsort.c
* Version: 1.0
*/
#include <pascal.h>
#include <MacTypes.h>
#include <QuickDraw.h>
#include <ResourceMgr.h>
#include <MenuMgr.h>
#include <MemoryMgr.h>
#include <FileMgr.h>
#include <HFS.h>
#include <ToolboxUtil.h>
#include <EventMgr.h>
#include <ListMgr.h>
#include <DialogMgr.h>
#include <StdFilePkg.h>
#include <OSUtil.h>
#include <SegmentLdr.h>
/* Low memory globals */
extern int ROM85 : 0x28E; /* ROM Version */
extern int mBarHeight : 0xBAA; /* menu bar height: 128K ROMs and onwards... */
/* A define to test for a 64K ROM. */
#define has64K (ROM85&0x8000)
/* A define to calculate the height of the menu bar. */
#define mbHeight ((has64K) ? 20 : mBarHeight)
/* These two are more efficient than using the Toolbox equivalents. */
#define HiWord(x) (((unsigned short *)&(x))[0])
#define LoWord(x) (((unsigned short *)&(x))[1])
/* LPOINT: macro that coerces a point into a long and vice versa. */
#define LPOINT(pt) (*((long*)&pt))
#define MIN(a,b) ((a < b) ? a : b)
/* A define to round up to an even number. */
#define EVEN(x) (( (x + 1) >> 1) << 1)
#define NAME_SIZE 32 /* Maximum Finder name length */
#define NULL 0L
/* A define to skip around the variable-length ESTR in the APPL resource. */
#define NEXT_APPL(a) ((Appl *)(EVEN( (ulong)(&a->name + a->name[0] + 1))))
/* Resource identities */
#define R_DLOG 128 /* The dialog box */
#define R_CORRUPT 129 /* The bad desktop alert */
#define R_NOHFS 130 /* The MFS File Manager is active alert */
#define R_VOLPROBS 131 /* Something's wrong with volumes. */
#define R_DESKTOP 128 /* The "Desktop" string */
#define R_FINDER 129 /* The "Finder" string */
#define R_ABOUT 132 /* The "About HD Runner..." dialog box */
#define R_MENUBAR 256 /* The MBAR resource. */
#define R_READING 133 /* "Reading desktop file..." dialog box */
#define R_CONFIG 128 /* The 'GNRL' config resource. */
/* Menu IDs */
#define M_ABOUT 256
#define M_FILE 257
#define M_EDIT 258
/* Dialog item numbers for the modeless dialog. */
#define I_RUN 1
#define I_QUIT 2
#define I_OUTLINE 3
#define I_APPS 4
#define I_MINIFINDER 5
#define I_DOCS 6
/* And for the "About" dialog. */
#define I_ABOUTOUTLN 5
/* Cursor keys */
#define cursUp 0x1e
#define cursDown 0x1f
/* Types */
typedef unsigned long ulong;
/* The layout of the 'APPL' resource in the desktop, as determined from TechNote #29,
* and confirmed by looking at its 'TMPL' resource.
*/
typedef struct {
ResType creator;
long dir;
unsigned char name[1];
} Appl, **ApplHandle;
/* The application table entry. Essentially, it's just the Appl struct above with a fixed size name
* field to make sorting easier.
*/
typedef struct {
int volID;
ResType creator;
long dir;
unsigned char name[NAME_SIZE];
} FullAppl;
/*
* The AppParmHandle layout for a single attached document.
*/
typedef struct {
int message;
int count;
AppFile a;
} OneArg;
/* Forward references */
Handle _Get1Resource(ResType, int);
int appCompare(FullAppl *, FullAppl *);
DialogPtr DispChooseApp();
pascal void ListDraw();
int StartsWith(Byte);
void Quit(void);
pascal Boolean ListFileFilter(ParmBlkPtr p);
void SetupDAs(void);
int DoCommand(long mResult);
int DoChar(DialogPtr dPtr, unsigned char theChar);
void About(void);
int EventLoop(DialogPtr, EventRecord *);
/* Globals */
int theSelection = 0; /* index of currently selected application */
int numApps; /* number of applications in list */
Rect box; /* scrolling list's outline */
ListHandle lh; /* List Manager handle */
FullAppl *apps = NULL; /* The application table. */
char fName[20]; /* Holds finder name retrieved from the R_FINDER 'STR ' */
Byte miniFinder; /* Is the Finder active? */
Handle mHandle; /* Handle to the menu bar. */
main()
{
int rsrc;
ApplHandle aH;
register Appl *a;
register Appl *end;
register FullAppl *copy;
Str255 name;
register int i;
int itemHit;
OSErr err;
WDPBRec pb;
HParamBlockRec v;
VolumeParam vparm;
int volNo;
StringHandle desktop, finder;
Handle config;
int volApps;
CursHandle watch;
int op;
SFReply reply;
Point pt;
DialogPtr dPtr;
EventRecord evRec;
DialogPtr reading;
int doDeskDialog;
/* Initialize the various managers. */
InitGraf(&thePort);
InitFonts();
FlushEvents( everyEvent, 0 );
InitWindows();
InitMenus();
TEInit();
InitDialogs(0L);
InitCursor();
MaxApplZone();
SetupDAs();
if (FSFCBLen == -1) { /* Is the HFS File Manager active? */
StopAlert(R_NOHFS, (ProcPtr)NULL);
ExitToShell();
}
if ( (finder = (StringHandle)GetResource('STR ', R_FINDER)) == NULL) {
/* ResEdit fiend on the loose? */
SysBeep(5);
ExitToShell();
}
BlockMove(*finder, fName, **finder +1);
ReleaseResource(fName);
/* Is the finder active? */
if ((miniFinder = IUEqualString(fName, FinderName)) != 0) {
/* Somebody's intercepting, but is it us? */
if (IUEqualString(FinderName, CurApName) != 0) {
/* Somebody else. miniFinder's cleared, and arrangements made to quit back to
* this guy, rather than the finder. This means that you can run HD Runner
* under Oasis, and then go back to Oasis afterwards.
*/
BlockMove(FinderName, fName, FinderName[0] +1);
miniFinder = 0;
}
}
if ( (config = GetResource('GNRL', R_CONFIG)) == NULL)
doDeskDialog = 0;
else
doDeskDialog = **config;
if (doDeskDialog) {
reading = GetNewDialog(R_READING, (Ptr)NULL, (WindowPtr)-1);
DrawDialog(reading); /* so that the string gets drawn. */
}
if ( (desktop = (StringHandle)GetResource('STR ', R_DESKTOP)) == NULL) {
/* ResEdit fiend on the loose? */
SysBeep(5);
Quit();
}
HNoPurge(desktop); /* just in case */
numApps = 0;
if ( (watch = GetCursor(watchCursor)) != NULL) SetCursor(*watch);
/* Run through all mounted volumes. */
for(volNo =1;; volNo++) {
v.volumeParam.ioCompletion = NULL;
v.volumeParam.ioNamePtr = NULL;
v.volumeParam.ioVolIndex = volNo;
v.volumeParam.ioVRefNum = 0;
err = PBHGetVInfo(&v, FALSE);
if (err == nsvErr) break; /* Run through all the mounted volumes. */
if (err != noErr) { /* Something's twisted. */
StopAlert(R_VOLPROBS, (ProcPtr)NULL);
Quit();
}
/* Validate the volume as online, HFS. */
if(v.volumeParam.ioVSigWord != 0x4244) continue; /* skip MFS */
if(v.volumeParam.ioVDRefNum >= 0) continue; /* offline */
/* This volume passes the tests. Let's go for its desktop file. Go for root. */
pb.ioCompletion = NULL;
pb.ioNamePtr = NULL;
pb.ioVRefNum = v.volumeParam.ioVRefNum;
if (PBSetVol(&pb, FALSE) != noErr) {
/* No such volume? I suspect that things are twisted, and there's no point
* in continuing.
*/
StopAlert(R_VOLPROBS, (ProcPtr)NULL);
Quit();
}
HLock(desktop);
SetResLoad(FALSE);
if ( (rsrc = OpenResFile(*desktop)) == -1) {
/* Finder hasn't put a desktop on this volume yet? */
SetResLoad(TRUE);
HUnlock(desktop);
continue;
}
HUnlock(desktop);
if ( (aH =(ApplHandle) _Get1Resource('APPL', 0)) == NULL) {
/* Is this a corrupt desktop? Skip it for now, since it may just be a new
* disk.
*/
SetResLoad(TRUE);
CloseResFile(rsrc);
continue;
}
SetResLoad(TRUE);
LoadResource(aH);
HLock(aH);
end = (Appl *) ( (char *)*aH + GetHandleSize(aH));
/* How many are there? */
for(a = *aH, volApps = 0; a < end;a = NEXT_APPL(a)) {
volApps++; /* total, this volume */
}
/* Get ready to copy to our application table. We do what's usually a major no-no,
* and grow non-relocatables. This is a controlled environment where we can get
* away with it (Famous last words...)
*/
if (apps == NULL) { /* First time */
if ( (apps = (FullAppl *)NewPtr((Size)(volApps * sizeof(FullAppl)))) == NULL) {
SysBeep(10);
CloseResFile(rsrc);
Quit();
}
} else { /* Grow it. */
SetPtrSize(apps, (Size)((numApps + volApps) * sizeof(FullAppl)));
if (MemErr != noErr) {
SysBeep(10);
CloseResFile(rsrc);
Quit();
}
}
/* Copy over the application data to the application table */
for(a = *aH, i = numApps, copy = apps + numApps; a < end;a = NEXT_APPL(a), copy++) {
copy->volID = v.volumeParam.ioVRefNum;
BlockMove(a, ©->creator, (Size)(sizeof(Appl) + a->name[0]));
}
numApps += volApps; /* total, all volumes */
/* Can close this desktop now */
HUnlock(aH);
ReleaseResource(aH);
CloseResFile(rsrc);
}
/* Did we find any applications on online HFS volumes? */
if (numApps == 0) Quit();
/* And quicksort them. */
if (qsort(apps, numApps, sizeof(FullAppl), appCompare) != 0) {
SysBeep(5);
Quit();
}
if (doDeskDialog) DisposDialog(reading);
SetCursor(&arrow);
/* Display the modeless dialog box, and then start the event loop. */
for(;;) {
dPtr = DispChooseApp();
op = EventLoop(dPtr, &evRec);
LDispose(lh);
DisposDialog(dPtr);
if (op == I_DOCS) {
SetPt(&pt, 70, 70);
SFGetFile(pt, "", ListFileFilter, -1, NULL, NULL, &reply);
/* Go around again to resolve the ambiguity about whatever Cancel means. */
if (reply.good == FALSE) continue;
}
break;
}
if (op != I_QUIT) { /* we have something to launch. */
/* Follow the example led by StdFile, and set up a HFS working directory. Then SetVol
* the sucker and launch.
*/
pb.ioCompletion = NULL;
pb.ioNamePtr = NULL;
pb.ioVRefNum = apps[theSelection].volID;
pb.ioWDProcID = 'H╢R~'; /* application signature. */
pb.ioWDDirID = apps[theSelection].dir;
if (PBOpenWD(&pb, FALSE) != noErr) {
StopAlert(R_CORRUPT, (ProcPtr)NULL );
Quit();
}
vparm.ioVRefNum = pb.ioVRefNum;
vparm.ioCompletion = NULL;
vparm.ioNamePtr = NULL;
if (PBSetVol(&vparm, FALSE) != noErr) {
StopAlert(R_VOLPROBS, (ProcPtr)NULL );
} else {
/* Opened the folder, so we can launch. */
if (miniFinder && CurApName[0] < 15) { /* i.e. don't rename! */
/* Arrange to return in MiniFinder style. */
BlockMove(CurApName, FinderName, CurApName[0] +1);
} else {
/* Make sure the Finder runs. */
BlockMove(fName, FinderName, fName[0] + 1);
}
if (op == I_DOCS) {
if (AppParmHandle != NULL) {
/* Attach the document to the application parameters. Start by adjusting the
* size of AppParmHandle to reflect the document name size, then copy in.
*/
SetHandleSize(AppParmHandle, (Size)(13 + reply.fName[0]));
if (MemErr != noErr) {
SysBeep(1); SysBeep(1); Quit();
}
(*((OneArg **)AppParmHandle))->message = appOpen;
(*((OneArg **)AppParmHandle))->count = 1;
(*((OneArg **)AppParmHandle))->a.versNum = 0;
BlockMove(reply.fName, (*((OneArg **)AppParmHandle))->a.fName,
(Size)(reply.fName[0] + 1));
(*((OneArg **)AppParmHandle))->a.fType = reply.fType;
(*((OneArg **)AppParmHandle))->a.vRefNum = reply.vRefNum;
}
}
/* We'd better check that it's there before we leap into the wild blue yonder... */
v.fileParam.ioCompletion = NULL;
v.fileParam.ioNamePtr = apps[theSelection].name;
v.fileParam.ioVRefNum = pb.ioVRefNum;
v.fileParam.ioFVersNum = 0;
v.fileParam.ioFDirIndex = 0;
if (PBGetFInfo(&v, FALSE) != noErr)
StopAlert(R_CORRUPT, (ProcPtr)NULL );
else
Launch(0, apps[theSelection].name);
}
}
Quit(); /* To the Finder. */
}
/*
* EventLoop: sufficient event loop processing for modeless dialogs and desk accessories.
*/
int
EventLoop(dPtr, ev)
register DialogPtr dPtr;
register EventRecord *ev;
{
short itemHit;
Handle theHand;
Rect miniBox;
short itemType;
WindowPtr whichWindow;
DialogPtr theDialog;
register char theChar;
Point whereIsIt;
Point cell;
GrafPtr savePort;
Rect dragRect;
Boolean editDim = TRUE; /* The edit menu starts out life as dimmed. */
while(1) {
SystemTask(); /* Clock keeps on ticking... */
/* Menu consistency. The edit menu should be disabled unless the top window is a
* system window.
*/
if (dPtr == FrontWindow() ) { /* Disable them. */
if (editDim == FALSE) {
editDim = TRUE;
DisableItem(GetMHandle(M_EDIT), 0);
DrawMenuBar();
}
} else { /* Enable them. */
if (editDim == TRUE) {
editDim = FALSE;
EnableItem(GetMHandle(M_EDIT), 0);
DrawMenuBar();
}
}
/* What's next? */
(void)GetNextEvent(everyEvent, ev);
/* First thing we do is check for dialog events. */
if (IsDialogEvent(ev) == TRUE) {
/* Do our own processing for stuff that DialogSelect won't do. */
switch(ev->what) {
case keyDown:
case autoKey:
theChar = ev->message & charCodeMask;
if ((ev->modifiers & cmdKey) != 0) {
if ((itemHit = DoCommand( MenuKey( theChar ))) != 0) return itemHit;
} else {
if ( (itemHit = DoChar(dPtr, theChar)) != 0) return itemHit;
}
continue;
case activateEvt:
LActivate(ev->modifiers&1, lh);
break;
}
if (DialogSelect(ev, &theDialog, &itemHit) == TRUE) {
GetPort(&savePort);
SetPort(dPtr);
switch(itemHit) {
case I_MINIFINDER:
miniFinder ^= 1;
GetDItem(dPtr, I_MINIFINDER, &itemType, &theHand, &miniBox);
SetCtlValue((ControlHandle)theHand, miniFinder);
break;
case I_APPS:
whereIsIt = ev->where; /* don't trash original */
GlobalToLocal( &whereIsIt );
/* LClick will follow the mouse until it's released. */
if (LClick(whereIsIt, 0, lh) == TRUE) { /* Double-click somewhere. */
LPOINT(cell) = LLastClick(lh);
theSelection = cell.v;
return( I_RUN );
}
/* Single click. Find out where the selection is now. Can't use LLastClick
* because we're interested in where the mouse was *released*.
*/
cell.v = 0;
cell.h = 0;
if (LGetSelect( TRUE, &cell, lh) == FALSE) {
/* Insufficient applications to activate scroll bar, and the click is in
* the unused portion. This has got to be a floppy!
*/
SysBeep(1);
cell.v = theSelection;
LSetSelect(TRUE, cell, lh);
} else if (theSelection != cell.v) {
/* New file type has been selected */
theSelection = cell.v;
}
break;
case I_RUN:
case I_QUIT:
case I_DOCS:
return itemHit;
default:
break;
}
SetPort(savePort);
}
continue;
}
/* Doesn't belong to the dialog. Do the normal event processing. */
switch (ev->what) {
case mouseDown:
switch (FindWindow( ev->where, &whichWindow )) {
case inDesk:
SysBeep(10);
break;
case inMenuBar:
if ((itemHit = DoCommand( MenuSelect(ev->where))) != 0)return itemHit;
case inSysWindow:
SystemClick(ev, whichWindow );
break;
case inContent:
if (whichWindow != FrontWindow())
SelectWindow(whichWindow);
break;
case inDrag:
if ( dPtr == whichWindow ) {
dragRect = screenBits.bounds;
InsetRect(&dragRect, 4, 4);
dragRect.top += mbHeight;
DragWindow( whichWindow, ev->where, &dragRect );
}
break;
}
break;
case keyDown:
case autoKey:
theChar = ev->message & charCodeMask;
if ((ev->modifiers & cmdKey) != 0) {
if ( (itemHit = DoCommand( MenuKey( theChar ))) !=0) return itemHit;
}
break;
}
}
}
/*
* DoChar: handles typing at the dialog.
*/
int
DoChar(dPtr, theChar)
DialogPtr dPtr;
unsigned char theChar;
{
int newSelect;
GrafPtr savePort;
Point cell;
/* Is theChar return or enter? */
if (theChar == 0x03 || theChar == 0x0d) return (I_RUN);
/* Take the char, and find the first application that starts with this char. */
GetPort(&savePort);
SetPort(dPtr);
if ((newSelect = StartsWith(theChar)) != theSelection) {
/* We've moved. Unhighlight the old. */
cell.h = 0;
cell.v = theSelection;
LSetSelect(FALSE, cell, lh);
/* Highlight the new. */
theSelection = cell.v = newSelect;
LSetSelect(TRUE, cell, lh);
/* And arrange to scroll until visible */
LAutoScroll(lh);
}
SetPort(savePort);
return 0;
}
/*
* SetupDAs: setup the desk accessory, file and edit menus.
*/
void
SetupDAs()
{
if ( (mHandle = GetNewMBar(R_MENUBAR)) == NULL) Quit();
SetMenuBar(mHandle);
/* And add the desk accessories. */
AddResMenu(GetMHandle(M_ABOUT), 'DRVR' );
DrawMenuBar();
}
/*
* DoCommand: fields menu commands.
*/
int
DoCommand( mResult)
long mResult;
{
register int theItem;
register int ret = 0;
Str255 name;
theItem = LoWord( mResult );
switch (HiWord(mResult)) {
case M_ABOUT:
if (theItem == 1) { /* It's the about dialog box. */
About();
} else {
GetItem(GetMHandle(M_ABOUT), theItem, name);
OpenDeskAcc( &name );
}
break;
case M_FILE:
/* The items here are Attach Document, a separator, and Quit. */
ret = (theItem == 1) ? I_DOCS : I_QUIT;
break;
case M_EDIT:
/* They're only there for the desk accessories. */
(void)SystemEdit(theItem-1);
break;
}
HiliteMenu(0);
return ret;
}
/*
* About: field an "About HD Runner" request.
*/
void
About()
{
DialogPtr about = GetNewDialog(R_ABOUT, (Ptr)NULL, (WindowPtr)-1);
short itemHit;
OutlineButton(about, OK, I_ABOUTOUTLN); /* Highlight the default button. */
ShowWindow(about);
do {
ModalDialog((ProcPtr)NULL, &itemHit);
} while (itemHit != OK);
DisposDialog(about);
}
/*
* Quit: an ExitToShell that ensures that we go to the finder.
*/
void
Quit()
{
BlockMove(fName, FinderName, fName[0] + 1);
ExitToShell();
}
/*
* The file filtering routine. Watch the conditions -- it's FALSE to display.
*/
pascal Boolean
ListFileFilter(p)
ParmBlkPtr p;
{
if ( p->fileParam.ioFlFndrInfo.fdCreator == apps[theSelection].creator &&
p->fileParam.ioFlFndrInfo.fdType != 'APPL' ) {/* It matched! */
return FALSE;
} else {
return TRUE; /* Do not display */
}
}
/*
* DispChooseApp: brings up the modeless dialog box.
*/
DialogPtr
DispChooseApp()
{
DialogPtr dPtr = GetNewDialog(R_DLOG, (Ptr)NULL, (WindowPtr)-1);
Handle theHand;
int itemType;
Point csize, cell;
Rect databounds;
Rect listRect;
register int i;
Rect miniBox;
/* Install the useritem drawing routine. */
GetDItem(dPtr, I_APPS, &itemType, &theHand, &box);
SetDItem(dPtr, I_APPS, itemType, ListDraw, &box);
/* Initialize parameters for LNew */
listRect = box; /* struct copy */
InsetRect(&listRect, 1, 1); /* prepare for FrameRect */
listRect.right -= 15; /* make room for horiz. scroll bar */
SetRect(&databounds, 0, 0, 1, 0); /* list dimensions == 1 row. */
csize.h = 0; /* init csize for defaults */
csize.v = 0;
/* Declare list and get a handle to it. */
lh = LNew(&listRect, &databounds, csize, 0, dPtr, FALSE, FALSE, FALSE, TRUE);
/* Specify click, drag, shift-click behavior */
(*lh)->selFlags = lOnlyOne | lNoNilHilite | lNoExtend;
/* Declare sufficient cells for our purpose. */
LAddRow(numApps, 0, lh);
/* Insert the application names into the cells. */
cell.h = 0;
cell.v = 0;
for(i = 0; i < numApps; i++, cell.v++) {
LSetCell(apps[i].name +1, (int)apps[i].name[0], cell, lh);
}
/* And set up the initial minifinder state. */
GetDItem(dPtr, I_MINIFINDER, &itemType, &theHand, &miniBox);
SetCtlValue((ControlHandle)theHand, miniFinder);
/* Arrange for highlighting of the default button. */
OutlineButton(dPtr, I_RUN, I_OUTLINE);
/* And make everything visible */
ShowWindow(dPtr);
/* Enable drawing */
LDoDraw(TRUE, lh);
/* Mark the initial selection (first application, first time, then whatever it was we
* cancelled an Attach Doc on.)
*/
cell.h = 0;
cell.v = theSelection;
LSetSelect(TRUE, cell, lh);
/* If we aren't at the beginning, arrange to scroll until visible */
if (theSelection != 0) LAutoScroll(lh);
return(dPtr);
}
/*
* And the draw routine for the user item.
*/
pascal void
ListDraw(theDialog, theItem)
DialogPtr theDialog;
int theItem;
{
FrameRect(&box); /* Draw the containing box. */
/* And tell the List Manager it's time to redraw. */
LUpdate( ((WindowPeek)theDialog)->port.visRgn, lh);
}
/*
* _Get1Resource: a GetResource for the current resource file only.
*/
Handle
_Get1Resource(res, id)
ResType res;
int id;
{
register Handle h;
if (has64K) {
h = GetResource(res, id);
return( (HomeResFile(h) != CurMap) ? (Handle)NULL : h);
} else {
return (Get1Resource(res, id));
}
}
/*
* appCompare: qsort comparison function.
*/
int
appCompare(a1, a2)
FullAppl *a1;
FullAppl *a2;
{
Str255 name;
/* Do a name comparison. */
return (IUCompString(a1->name, a2->name));
}
/*
* StartsWith: utility routine to do first character match for scrolling. Matches up to
* MAX_MATCH characters and then clamps until timeout.
*/
/* State data for multiple character match. */
#define MAX_MATCH 4
#define KEY_MAGNIFIER 2 /* The value used by StdFile. Thanks, MacNosy! */
static int matchLen = 1;
static char match[MAX_MATCH +1];
static ulong lastKey = 0;
int
StartsWith(c)
Byte c;
{
register int i;
unsigned char pre[MAX_MATCH +1];
register int cmpLen;
register ulong thresh;
register FullAppl *a;
/* Check for cursor up, down. */
if (c == cursDown) {
/* Go up one, clamp at the end. */
lastKey = Ticks;
matchLen = 1;
return ((theSelection == numApps -1)? theSelection : theSelection +1);
}
if (c == cursUp) {
lastKey = Ticks;
matchLen = 1;
/* Go down one, clamp at the bottom. */
return((theSelection == 0)? 0: theSelection -1);
}
if (lastKey != 0) {
/* Have a timestamp. Are we still within the time interval for matching? */
thresh = (ulong)Ticks - (ulong) (KeyThresh * KEY_MAGNIFIER);
if (thresh < lastKey) {
/* Within the interval. Check the MAX_MATCH clamp. */
if (matchLen +1<= MAX_MATCH) {
/* Add another character to the match string. */
matchLen++;
}
} else {
/* Waited too long. Reset the count. */
matchLen = 1;
}
} else {
/* Starting things off. */
matchLen = 1;
}
lastKey = (ulong) Ticks;
match[0] = matchLen;
match[matchLen] = c;
UprString(match, FALSE);
/* Use the International Utilities package to handle case differences. */
for(i = 0, a = apps; i < numApps; i++, a++) {
cmpLen = MIN(matchLen, apps[i].name[0]);
BlockMove(a->name +1, pre +1, cmpLen);
pre[0] = cmpLen;
UprString(pre, FALSE);
if (pstrncmp(pre +1, match +1, cmpLen) >= 0) return i;
}
return (numApps -1);
}
/*
* Pascal string version of strncmp. Used for comparing processed strings (UprString) & avoiding
* the overhead of calling IUMagString. Not very general purpose - it assumes that each string is
* at least n chars long.
*/
int
pstrncmp(s1, s2, n)
register char *s1, *s2;
register int n;
{
for (; --n && (*s1 == *s2); s1++, s2++) ;
return (*s1 - *s2);
}